package com.soxing.webrain.brush.common.util;

import com.soxing.webrain.brush.common.constant.SystemConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author luqiang
 */
@Slf4j
public class FileUtil {

    public final static String SEPARATOR = "/";
    private final static int FILE_NAME_MAX_LENGTH = 15;

    /**
     * 指定位置开始始写入文件
     *
     * @param tempFile 输入文件
     * @param outPath  输出文件的路径
     * @throws IOException
     */
    public static void randomAccessFile(String outPath, MultipartFile tempFile, boolean append) throws IOException {
        RandomAccessFile raFile = null;
        BufferedInputStream inputStream = null;
        try {
            File dirFile = new File(outPath);
            if (!dirFile.exists()) {
                dirFile.createNewFile();
            }
            // 以读写的方式打开目标文件
            raFile = new RandomAccessFile(dirFile, "rw");
            if (append) {
                raFile.seek(raFile.length());
            } else {
                raFile.seek(0);
            }

            inputStream = new BufferedInputStream(tempFile.getInputStream());
            byte[] buf = new byte[1024];
            int length = 0;
            while ((length = inputStream.read(buf)) != -1) {
                raFile.write(buf, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new IOException(e.getMessage());
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (raFile != null) {
                    raFile.close();
                }
            } catch (Exception e) {
                throw new IOException(e.getMessage());
            }
        }
    }

    /**
     * 文件保存
     *
     * @param outPath
     * @param tempFile
     */
    public static void saveFile(String outPath, MultipartFile tempFile) {
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(outPath);
            IOUtils.copy(tempFile.getInputStream(), outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 计算文件绝对路径
     *
     * @param dirPath  基准路径
     * @param maxFiles 每个文件夹的最大文件数
     * @param prefix   文件前缀
     * @param fileName 文件名
     * @return
     */
    public static String calcFilePath(String dirPath, int maxFiles, String prefix, String fileName) {
        File dirFile = new File(dirPath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        int max = 0;
        String[] fArr = dirFile.list();
        if (fArr == null || fArr.length <= 0) {
        } else {
            for (String f : fArr) {
                try {
                    Integer i = Integer.valueOf(f);
                    if (i > max) {
                        max = i;
                    }
                } catch (Exception e) {
                    // e.printStackTrace();
                }
            }
        }
        File maxDir = new File(dirPath + SEPARATOR + max);
        if (!maxDir.exists()) {
            maxDir.mkdir();
        }
        String[] tmpArr = maxDir.list();
        if (tmpArr != null && tmpArr.length >= maxFiles) {
            max = max + 1;
            maxDir = new File(dirPath + SEPARATOR + max);
            maxDir.mkdir();
        }
        fileName = StringUtils.removeSpecialChar(fileName);
        return dirPath + SEPARATOR + max + SEPARATOR + prefix + "-" + fileName;
    }

    /**
     * 计算任务结果文件路径
     *
     * @param dirPath
     * @param userPin
     * @param projectId
     * @param projectName
     * @return
     */
    public static String calcTaskResultFilePath(String dirPath, String userPin, Integer projectId, String projectName) {
        projectName = StringUtils.removeSpecialChar(projectName);
        return dirPath + SEPARATOR + userPin + SEPARATOR + projectId + "-" + projectName;
    }

    /**
     * 文件md5计算
     *
     * @param file
     * @return
     */
    public static String getMd5(File file) {
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            return DigestUtils.md5Hex(fileInputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 文件md5计算
     *
     * @param file
     * @return
     */
    public static String calcFileMd5(File file) {
        FileInputStream fileInputStream = null;
        try {
            MessageDigest MD5 = MessageDigest.getInstance("MD5");
            fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[8192];
            int length;
            while ((length = fileInputStream.read(buffer)) != -1) {
                MD5.update(buffer, 0, length);
            }
            return new String(Hex.encodeHex(MD5.digest()));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 下载服务器已存在的文件,不能断点续传
     *
     * @param request
     * @param response
     * @param proposeFile
     * @throws IOException
     * @throws FileNotFoundException
     */
    public static void downloadExistsFile(HttpServletRequest request, HttpServletResponse response, File proposeFile)
            throws IOException, FileNotFoundException {
        long fSize = proposeFile.length();
        // 下载
        response.setContentType("application/x-download");
        // response.setCharacterEncoding("UTF-8");
        String isoFileName = URLEncoder.encode(proposeFile.getName(), "UTF-8");
        // response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Length", String.valueOf(fSize));
        response.setHeader("Content-Disposition", "attachment; filename=" + isoFileName);

        OutputStream outputStream = null;
        InputStream inputStream = null;
        try {
            outputStream = new BufferedOutputStream(response.getOutputStream());
            inputStream = new BufferedInputStream(new FileInputStream(proposeFile));
            byte[] buffer = new byte[1024];

            int len = 0;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * shell执行日志路径计算
     *
     * @param dirPath  日志根路径
     * @param type     命令类型 sysfulltem,project
     * @param maxFiles 目录最大文件数
     * @param prefix   前缀
     * @param fileName 文件名
     * @return
     */
    public static String calShellLogPath(String dirPath, String type, int maxFiles, String prefix, String fileName) {
        String baseDir = dirPath + SEPARATOR + type;
        return calcFilePath(baseDir, maxFiles, prefix, fileName);
    }

    /**
     * 文件遍历
     *
     * @param path
     */
    public static void traverseFolder(String path) {
        File file = new File(path);
        if (file.exists()) {
            File[] files = file.listFiles();
            if (files.length == 0) {
                System.out.println("文件夹是空的!");
                return;
            } else {
                for (File file2 : files) {
                    if (file2.isDirectory()) {
                        System.out.println("文件夹:" + file2.getAbsolutePath());
                        traverseFolder(file2.getAbsolutePath());
                    } else {
                        System.out.println("文件:" + file2.getAbsolutePath());
                    }
                }
            }
        } else {
            System.out.println("文件不存在!");
        }
    }

    /**
     * 读取文件最后N行
     * <p>
     * 根据换行符判断当前的行数， 使用统计来判断当前读取第N行
     * <p>
     * PS:输出的List是倒叙，需要对List反转输出
     *
     * @param file    待文件
     * @param numRead 读取的行数
     * @return List<String>
     */
    public static List<String> readLastNLine(File file, long numRead) {
        // 定义结果集
        List<String> result = new ArrayList<String>();
        // 行数统计
        long count = 0;

        // 排除不可读状态
        if (!file.exists() || file.isDirectory() || !file.canRead()) {
            return null;
        }

        // 使用随机读取
        RandomAccessFile fileRead = null;
        try {
            // 使用读模式
            fileRead = new RandomAccessFile(file, "r");
            // 读取文件长度
            long length = fileRead.length();
            // 如果是0，代表是空文件，直接返回空结果
            if (length == 0L) {
                return result;
            } else {
                // 初始化游标
                long pos = length - 1;
                while (pos > 0) {
                    pos--;
                    // 开始读取
                    fileRead.seek(pos);
                    // 如果读取到\n代表是读取到一行
                    if (fileRead.readByte() == '\n') {
                        // 使用readLine获取当前行
                        String line = fileRead.readLine();
                        // 保存结果
                        if (!"".equals(line.trim())) {
                            result.add(line);
                        }
                        // 打印当前行
                        log.info(line);

                        // 行数统计，如果到达了numRead指定的行数，就跳出循环
                        count++;
                        if (count == numRead) {
                            break;
                        }
                    }
                }
                if (pos == 0) {
                    fileRead.seek(0);
                    result.add(fileRead.readLine());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileRead != null) {
                try {
                    // 关闭资源
                    fileRead.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * 通过文件名后缀获得文件格式
     *
     * @return
     */
    public static String getFileFormat(boolean isDir, String path) {
        if (isDir) {
            return ".dir";
        }
        int dotIdx = path.lastIndexOf(".");
        if (dotIdx > 0) {
            return path.substring(dotIdx);
        }
        return ".null";
    }

    public static String getFileHtml(File file, int i) {
        Date ldate = new Date(file.lastModified());
        String lastModified = DateUtil.dateFormat(ldate, DateUtil.DATEFORMAT_SECOND);
        double size = file.length() / (1024.0 * 1024);
        DecimalFormat df = new DecimalFormat("######0.00");
        String strSize = df.format(size) + "M";
        if (file.isDirectory()) {
            strSize = " ";
        }

        String fileName = file.getName();
        if (fileName.length() > FILE_NAME_MAX_LENGTH) {
            fileName = "..." + fileName.substring(fileName.length() - FILE_NAME_MAX_LENGTH, fileName.length());
        }

        String fullpath = file.getPath();
        if (fullpath == null) {
            fullpath = "0";
        }

        String DESfullpath = AddressDesUtil.getEncryptString(fullpath);

        String tmpStr = SystemConstants.LI_TEMPLATE.replace("${dataId}", String.valueOf(i + 1))
                .replace("${FileName}", fileName).replace("${Size}", strSize).replace("${lastModified}", lastModified)
                .replace("${isDir}", file.isDirectory() ? "fa fa-folder" : "fa fa-file")
                .replace("${FullPath}", DESfullpath)
                .replace("${isView}", fullpath.equals("0") || (!fullpath.toLowerCase().endsWith(".mat") && !fullpath.toLowerCase().endsWith(".zip") && !file.isDirectory()) ? "none" : "");
        return tmpStr;
    }

    public static String getFileHtmlForCheckbox(File file, int i) {
        Date ldate = new Date(file.lastModified());
        String lastModified = DateUtil.dateFormat(ldate, DateUtil.DATEFORMAT_SECOND);
        double size = file.length() / (1024.0 * 1024);
        DecimalFormat df = new DecimalFormat("######0.00");
        String strSize = df.format(size) + "M";
        if (file.isDirectory()) {
            strSize = " ";
        }

        String fileName = file.getName();
        if (fileName.length() > FILE_NAME_MAX_LENGTH) {
            fileName = "..." + fileName.substring(fileName.length() - FILE_NAME_MAX_LENGTH, fileName.length());
        }

        String fullpath = file.getPath();
        if (fullpath == null) {
            fullpath = "0";
        }

        String DESfullpath = AddressDesUtil.getEncryptString(fullpath);

        String tmpStr = SystemConstants.LI_TEMPLATE_FOR_CHECKBOX.replace("${dataId}", String.valueOf(i + 1))
                .replace("${FileName}", fileName).replace("${Size}", strSize).replace("${path}", file.getAbsolutePath())
                .replace("${lastModified}", lastModified)
                .replace("${isDir}", file.isDirectory() ? "fa fa-folder" : "fa fa-file")
                .replace("${FullPath}", DESfullpath)
                .replace("${isView}", fullpath.equals("0") || (!fullpath.toLowerCase().endsWith(".mat") && !fullpath.toLowerCase().endsWith(".zip") && !file.isDirectory()) ? "none" : "");
        return tmpStr;
    }

    /**
     * 文件遍历
     *
     * @param path       文件路径
     * @param i          编号
     * @param isCheckbox 是否需要生成checkbox
     * @return 一个特定格式的html
     */
    public static String traverseFolder(String path, int i, boolean isCheckbox) {
        StringBuilder sb = new StringBuilder();
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }

        // 返回一个抽象路径名数组，这些路径名表示此抽象路径名表示的目录中的文件
        File[] files = file.listFiles();
        if (files.length == 0 || file.isFile()) {
            if (isCheckbox) {
                sb.append(getFileHtmlForCheckbox(file, i));
            } else {
                sb.append(getFileHtml(file, i));
            }
            return sb.toString().replace("${ol}", "");
        } else {
            for (File file2 : files) {
                if (file2.isDirectory()) {
                    String bb = traverseFolder(file2.getAbsolutePath(), i++, isCheckbox);
                    sb.append(bb);
                } else {
                    String aa = null;
                    if (isCheckbox) {
                        aa = getFileHtmlForCheckbox(file2, i);
                    } else {
                        aa = getFileHtml(file2, i);
                    }
                    aa = aa.replace("${ol}", "");
                    sb.append(aa);
                }
            }
            String ol = SystemConstants.OL_TEMPLATE.replace("${li}", sb.toString());
            String cc = null;
            if (isCheckbox) {
                cc = getFileHtmlForCheckbox(file, i);
            } else {
                cc = getFileHtml(file, i);
            }
            cc = cc.replace("${ol}", ol);
            return cc;
        }
    }


    public static String getMyFileHtml(MyFile file, int i) {
        Date ldate = new Date();
        String lastModified = DateUtil.dateFormat(ldate, DateUtil.DATEFORMAT_SECOND);
        double size = file.getSize() / (1024.0 * 1024);
        DecimalFormat df = new DecimalFormat("######0.00");
        String strSize = df.format(size) + "M";
        if (file.isDir()) {
            strSize = " ";
        }

        String fileName = file.getFileName();
        if (fileName.length() > FILE_NAME_MAX_LENGTH) {
            fileName = "..." + fileName.substring(fileName.length() - FILE_NAME_MAX_LENGTH, fileName.length());
        }

        String fullpath = file.getFullPath();
        if (fullpath == null) {
            fullpath = "0";
        }

        String DESfullpath = AddressDesUtil.getEncryptString(fullpath);

        String tmpStr = SystemConstants.MYFILE_LI_TEMPLATE.replace("${dataId}", String.valueOf(i + 1))
                .replace("${FileName}", fileName).replace("${Size}", strSize).replace("${lastModified}", lastModified)
                .replace("${isDir}", file.isDir() ? "fa fa-folder" : "fa fa-file")
                .replace("${FullPath}", DESfullpath)
                .replace("${isView}", fullpath.equals("0") || (!fullpath.toLowerCase().endsWith(".mat") && !fullpath.toLowerCase().endsWith(".zip") && !file.isDir()) ? "none" : "");

        return tmpStr;
    }

    /**
     * zip文件遍历
     *
     * @param file
     */
    public static String traverseZipFile(MyFile file, int i) {
        StringBuilder sb = new StringBuilder();

        List<MyFile> files = file.getChildList();
        if (files == null || files.size() == 0 || !file.isDir()) {
            sb.append(getMyFileHtml(file, i));
            return sb.toString().replace("${ol}", "");
        } else {
            for (MyFile file2 : files) {
                if (file2.isDir()) {
                    String bb = traverseZipFile(file2, i++);
                    sb.append(bb);
                } else {
                    String aa = getMyFileHtml(file2, i);
                    aa = aa.replace("${ol}", "");
                    sb.append(aa);
                }
            }
            String ol = SystemConstants.OL_TEMPLATE.replace("${li}", sb.toString());
            String cc = getMyFileHtml(file, i);
            cc = cc.replace("${ol}", ol);
            return cc;
        }
    }

    public static void main(String[] args) throws Exception {
        File file = new File("C:\\export\\data\\combs.uestc.edu.cn\\ftp\\soxing\\vsftpd.conf");
        System.out.println(getMd5(file));
        System.out.println(calcFileMd5(file));
        System.out.println(DigestUtils.md5Hex(new FileInputStream(file)));
    }


}
